/*
 * DateChooser.java
 * 
 * Copyright (c) 2008, Marshall Scorcio, Ralph Holzmann, 
 *                     Stewart Sweet, Tyler Hoersch, Jeffery Van Handel
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University of Wisconsin Oshkosh nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import java.text.*;

public class DateChooser extends JDialog implements KeyListener, MouseListener, FocusListener, ItemListener, ActionListener
{
    private static final String[] MONTHS = 
        new String[] { "January", "February", "March", "April", "May", "June", 
                       "July", "August", "September", "October", "November", 
                       "December" };
    
    private static final String[] DAYS =
	    new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    
    private static final Color WEEK_DAYS_FOREGROUND = Color.BLACK;
    private static final Color DAYS_FOREGROUND = Color.BLUE;
    private static final Color SELECTED_DAY_FOREGROUND = Color.WHITE;
    private static final Color SELECTED_DAY_BACKGROUND = Color.BLUE;
    private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);
    private static final Border FOCUSED_BORDER = BorderFactory.createLineBorder(Color.YELLOW, 1);
    
    private static final int FIRST_YEAR = 2005;
    private static final int LAST_YEAR = 2020;
    
    private GregorianCalendar calendar;
    private JLabel[][] dayLabels;
    private FocusablePanel daysGrid;
    private JComboBox monthComboBox;
    private JComboBox yearComboBox;
    private JButton okButton;
    private JButton cancelButton;

    private SpinnerDateModel startTimeModel = new SpinnerDateModel(new Date(), null, null, Calendar.HOUR_OF_DAY);
    private SpinnerDateModel endTimeModel = new SpinnerDateModel((Date) startTimeModel.getNextValue(), null, null, Calendar.HOUR_OF_DAY);
    private JSpinner startTimeSpinner = new JSpinner(startTimeModel);
    private JSpinner endTimeSpinner = new JSpinner(endTimeModel);
    private JSpinner.DateEditor startTimeEditor = new JSpinner.DateEditor(startTimeSpinner, "hh:mm a");
    private JSpinner.DateEditor endTimeEditor = new JSpinner.DateEditor(endTimeSpinner, "hh:mm a");
    
    private JLabel startTimeLabel;
    private JLabel endTimeLabel;
    private JLabel dayLabel;
    private JLabel reasonLabel;
    private JTextField reasonTextField;
    
    private int offset;
    private int lastDay;
    private boolean dateChosen;
    private SimpleDateFormat dateFormat;
    
    private static class FocusablePanel extends JPanel
    {
        public FocusablePanel(LayoutManager layout)
        {
            super(layout);
        }
        
        public boolean isFocusable()
        {
            return true;
        }
    }
    
    public DateChooser(Dialog owner, String title)
    {
        super(owner, title, true);
        initComponents();
    }

    public DateChooser(Dialog owner)
    {
        super(owner, true);
        initComponents();
    }

    public DateChooser(Frame owner, String title)
    {
        super(owner, title, true);
        initComponents();
    }

    public DateChooser(Frame owner)
    {
        super(owner, true);
        initComponents();
    }

    private void initComponents()
    {
        dateChosen = false;
        
        calendar = new GregorianCalendar();
        dateFormat = new SimpleDateFormat("hh:mm a");

        monthComboBox = new JComboBox(MONTHS);
        monthComboBox.addItemListener(this);

        yearComboBox = new JComboBox();
        for (int i = FIRST_YEAR; i <= LAST_YEAR; i++)
        {
            yearComboBox.addItem(Integer.toString(i));
        }
        yearComboBox.addItemListener(this);

        dayLabels = new JLabel[7][7];
        for (int i = 0; i < 7; i++)
        {
            dayLabels[0][i] = new JLabel(DAYS[i], JLabel.RIGHT);
            dayLabels[0][i].setForeground(WEEK_DAYS_FOREGROUND);
        }
        for (int i = 1; i < 7; i++)
        {
            for (int j = 0; j < 7; j++)
            {
                dayLabels[i][j] = new JLabel(" ", JLabel.RIGHT);
                dayLabels[i][j].setForeground(DAYS_FOREGROUND);
                dayLabels[i][j].setBackground(SELECTED_DAY_BACKGROUND);
                dayLabels[i][j].setBorder(EMPTY_BORDER);
                dayLabels[i][j].addMouseListener(this);
            }
        }

        startTimeLabel = new JLabel("Start Time:");
        endTimeLabel = new JLabel("End Time:");

        startTimeSpinner.setEditor(startTimeEditor);
        endTimeSpinner.setEditor(endTimeEditor);
        
        reasonLabel = new JLabel("Reason:");
        reasonTextField = new JTextField(20);
            
        okButton = new JButton("Ok");
        okButton.addActionListener(this);
        cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(this);

        JPanel monthYearPanel = new JPanel();
        monthYearPanel.add(monthComboBox);
        monthYearPanel.add(yearComboBox);

        JPanel startEndTimePanel = new JPanel();
        startEndTimePanel.add(startTimeLabel);
        startEndTimePanel.add(startTimeSpinner);
        startEndTimePanel.add(endTimeLabel);
        startEndTimePanel.add(endTimeSpinner);
        
        JPanel reasonPanel = new JPanel();
        reasonPanel.add(reasonLabel);
        reasonPanel.add(reasonTextField);
        
        JPanel okCancelPanel = new JPanel();
        okCancelPanel.add(okButton);
        okCancelPanel.add(cancelButton);
        getRootPane().setDefaultButton(okButton);

        daysGrid = new FocusablePanel(new GridLayout(7, 7, 5, 0));
        daysGrid.addFocusListener(this);
        daysGrid.addKeyListener(this);
        for (int i = 0; i < 7; i++)
        {
            for (int j = 0; j < 7; j++)
            {
                daysGrid.add(dayLabels[i][j]);
            }
        }
        daysGrid.setBackground(Color.WHITE);
        daysGrid.setBorder(BorderFactory.createLoweredBevelBorder());
        JPanel daysPanel = new JPanel(new FlowLayout());
        daysPanel.add(daysGrid);

        JPanel bottomPanel = new JPanel(new GridLayout(3, 1));
        bottomPanel.add(startEndTimePanel);
        bottomPanel.add(reasonPanel);
        bottomPanel.add(okCancelPanel);
        
        Container dialog = getContentPane();
        dialog.add("North", monthYearPanel);
        dialog.add("Center", daysPanel);
        dialog.add("South", bottomPanel);

        Calendar calendar = Calendar.getInstance();
        monthComboBox.setSelectedIndex(calendar.get(Calendar.MONTH));
        yearComboBox.setSelectedIndex(calendar.get(Calendar.YEAR) - FIRST_YEAR);
        setSelectedDay(1);
        update();
        
        setMinimumSize(new Dimension(225, 300));
        pack();
        setResizable(false);
    }

    private int getSelectedDay()
    {
        int selectedDay = -1;
        
        if (dayLabel != null)
        {
            try
            {
                selectedDay =  Integer.parseInt(dayLabel.getText());
            } 
            catch (NumberFormatException e)
            {
            }
        }
        
        return selectedDay;
    }

    private void setSelectedDay(JLabel newDay)
    {
        if (dayLabel != null)
        {
            dayLabel.setForeground(DAYS_FOREGROUND);
            dayLabel.setOpaque(false);
            dayLabel.setBorder(EMPTY_BORDER);
        }
        
        dayLabel = newDay;
        dayLabel.setForeground(SELECTED_DAY_FOREGROUND);
        dayLabel.setOpaque(true);
        
        if (daysGrid.hasFocus())
        {
            dayLabel.setBorder(FOCUSED_BORDER);
        }
    }

    private void setSelectedDay(int newDay)
    {
	    setSelectedDay(dayLabels[(newDay + offset - 1)/ 7 + 1][(newDay + offset - 1) % 7]);
    }

    private void update()
    {
        int day = getSelectedDay();
        for (int i = 0; i < 7; i++)
        {
            dayLabels[1][i].setText(" ");
            dayLabels[5][i].setText(" ");
            dayLabels[6][i].setText(" ");
        }
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.MONTH, monthComboBox.getSelectedIndex() + Calendar.JANUARY);
        calendar.set(Calendar.YEAR, yearComboBox.getSelectedIndex() + FIRST_YEAR);

        offset = calendar.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY;
        lastDay = calendar.getActualMaximum(Calendar.DATE);
        
        for (int i = 0; i < lastDay; i++)
        {
            dayLabels[(i + offset) / 7 + 1][(i + offset) % 7].setText(String.valueOf(i + 1));
        }
        
        if (day != -1)
        {
            if (day > lastDay)
            {
                day = lastDay;
            }
            setSelectedDay(day);
        }
    }
    
    public boolean isDateChosen()
    {
        return dateChosen;
    }
   
    public int getDayOfWeek()
    {
       return (getSelectedDay() + offset - 1) % 7;
    }
    
    public String getReason()
    {
        return reasonTextField.getText();
    }
    
    public int getYear()
    {
        return Integer.parseInt((String) yearComboBox.getSelectedItem());
    }
    
    public String getMonth()
    {
        return monthComboBox.getSelectedItem().toString();
    }
    
    public int getDayOfMonth()
    {
        return getSelectedDay();
    }
    
    public String getStartTime()
    {
        return dateFormat.format(startTimeModel.getDate());
    }
    
    public String getEndTime()
    {
        return dateFormat.format(endTimeModel.getDate());
    }
    
    public String getFormattedDate()
    {
        return (Day.getNameFromDayOfWeek(getDayOfWeek()) + ", " + getMonth() + " "
                + getDayOfMonth() + ", " + getYear());
    }
    
    public String getFormattedTime()
    {
        return getStartTime() + " - " + getEndTime();
    }

    public void actionPerformed(ActionEvent event) 
    {            
        if (event.getSource() == okButton)
        {
            try
            {
                if (dateFormat.parse(getStartTime()).compareTo(dateFormat.parse(getEndTime())) < 0)
                {
                    dateChosen = true;
                    setVisible(false);
                }
                else
                {
                    JOptionPane.showMessageDialog(getOwner(), "Please select an end time that occurs later than the start time", 
                                                  "Start time is earlier than end time", JOptionPane.ERROR_MESSAGE);
                }
            }
            catch (ParseException exception)
            {
                System.err.println(exception);
            }
        }
        else if (event.getSource() == cancelButton)
        {
            dateChosen = false;
            setVisible(false);
        }
    }

    public void itemStateChanged(ItemEvent event)
    {
        update();
    }
    
    public void focusGained(FocusEvent event)
    {
        try
        {
            setSelectedDay(dayLabel);
        }
        catch(NullPointerException exception)
        {
        }
    }

    public void focusLost(FocusEvent event)
    {
        setSelectedDay(dayLabel);
    }
        
    public void keyPressed(KeyEvent event)
    {
        int iday = getSelectedDay();
        
        switch (event.getKeyCode())
        {
            case KeyEvent.VK_LEFT:
                if (iday > 1)
                {
                    setSelectedDay(iday - 1);
                }
                break;
                
            case KeyEvent.VK_RIGHT:
                if (iday < lastDay)
                {
                    setSelectedDay(iday + 1);
                }
                break;
                
            case KeyEvent.VK_UP:
                if (iday > 7)
                {
                    setSelectedDay(iday - 7);
                }
                break;
                
            case KeyEvent.VK_DOWN:
                if (iday <= lastDay-7)
                {
                    setSelectedDay(iday + 7);
                }
                break;
        }
    }

    public void keyReleased(KeyEvent event)
    {
    }
    
    public void keyTyped(KeyEvent event) 
    {
    }
    
    public void mouseClicked(MouseEvent event)
    {
        JLabel dayLabel = (JLabel) event.getSource();
        
        if (!dayLabel.getText().equals(" "))
        {
            setSelectedDay(dayLabel);
        }
        
        daysGrid.requestFocus();
    }
    
    public void mouseEntered(MouseEvent event) 
    {
    }
    
    public void mouseExited(MouseEvent event) 
    {
    }
    
    public void mousePressed(MouseEvent event) 
    {
    }
    
    public void mouseReleased(MouseEvent event) 
    {
    }
}

